package de.koller.aframe.util.noise.n3D;

import de.koller.aframe.util.math.Function3DPreview;
import de.koller.aframe.util.math.IFunction3D;
import de.koller.aframe.util.math.interpolation.CubicInterpolation;
import java.util.Random;

public class Noise3D implements IFunction3D {
	
	public final float[][][] noise;

	public Noise3D( Random rnd, int size ) {
		if( size < 4 )
			throw new IllegalArgumentException( "must have at least size 4" );
		this.noise = new float[size][size][size];
		
		for( int x = 0; x < noise.length; x++ )
			for( int y = 0; y < noise.length; y++ )
				for( int z = 0; z < noise.length; z++ )
					noise[x][y][z] = rnd.nextFloat();
	}
	
	public Noise3D( float[][][] noise ) {
		this.noise = noise;
	}
	
	@Override
	public float getValue( float x, float y, float z ) {
		if( x >= 1 ) x = 0.9999f;
		if( y >= 1 ) y = 0.9999f;
		if( y >= 1 ) z = 0.9999f;
		if( x < 0 ) x = 0;
		if( y < 0 ) y = 0;
		if( z < 0 ) z = 0;
		
		int a = noise.length - 3;
		x *= a;
		y *= a;
		z *= a;
		
		float zx = x % 1f;
		float zy = y % 1f;
		float zz = z % 1f;
		
		int dx = (int)Math.floor( x );
		int dy = (int)Math.floor( y );
		int dz = (int)Math.floor( z );
		
		return CubicInterpolation.tricubic( zx, zy, zz, dx, dy, dz, noise ); 
	}
	
	public void normalize( float height ) {
		float maxV = Float.MIN_VALUE;
		float minV = Float.MAX_VALUE;
		
		for( int x = 0; x < noise.length; x++ )
			for( int y = 0; y < noise.length; y++ )
				for( int z = 0; z < noise.length; z++ ) {
					maxV = Math.max( noise[x][y][z], maxV );
					minV = Math.min( noise[x][y][z], minV );
				}
		
		for( int x = 0; x < noise.length; x++ )
			for( int y = 0; y < noise.length; y++ )
				for( int z = 0; z < noise.length; z++ )
					noise[x][y][z] = (noise[x][y][z] - minV) * (height / (maxV-minV));
	}
	
	public void normalizeMaxBound( float maxHeight ) {
		for( int x = 0; x < noise.length; x++ )
			for( int y = 0; y < noise.length; y++ )
				for( int z = 0; z < noise.length; z++ )
					noise[x][y][z] *= (1f / maxHeight);
	}
	
	public static void main( String[] args ) {
		Random r = new Random();
		
		Noise3D n = new Noise3D( r, 10 );
		
		Function3DPreview.Z_STEPS = 100;
		Function3DPreview.PRINT_PROGRESS = true;
		Function3DPreview.showFunction( 0, 0, 0, 1, 1, 1, 0, 1, n );

	}
	
}
